#!/usr/bin/env python3
from typing import List, Tuple, ClassVar, Type, Any
from dataclasses import dataclass
from marshmallow import Schema

import xmlrpc.client
import marshmallow_dataclass
import pprint

try:
    from types import SimpleNamespace as Namespace
except ImportError:
    # Python 2.x fallback
    from argparse import Namespace


@dataclass
class Address:
    """All of the Dataclasses us Marshmallow for Serialisation
       This is the adress. It contains coordinates but also the adress"""

    id: int
    name: str
    phone: str
    street: str
    street2: str
    city: str
    zip: str
    longitude: float
    latitude: float
    # state: str


@dataclass
class Sale:
    """Unlike pythons dynamic Typing we use statically Typed Classes
       This allows us to catch TypeErrors and thus allows us to validate the
       Answer from the Server"""

    id: int
    name: str
    invoice_status: str
    amount_total: float
    amount_tax: float
    create_date: str
    order_line: List[int]
    # price_subtotal: float


@dataclass
class Sale_Line:
    """This class is one line of a sail.
       The relation Ship is many sale_lines to one sale"""

    id: int
    name: str
    price_total: float
    product_uom_qty: int


@dataclass
class Picking(object):
    """Pickings are drone flights which are created by Sales
        A Sale has three steps in a picking. It is picked, packed and then shiped
       currently a user in the Backend advances the picking in the web app.
       picking validation threw GPS or QR Codes is a feature automation 
    """

    id: int
    name: str
    group_id: Tuple[int, str]
    location_id: Tuple[int, str]
    location_dest_id: Tuple[int, str]
    move_lines: List[int]
    partner_id: Tuple[int, str]
    sale_id: Tuple[int, str]
    state: str
    create_date: str
    printed: bool
    product_id: Tuple[int, str]
    # wk_picking_notes: Any

    Schema: ClassVar[Type[Schema]] = Schema


PickingSchema = marshmallow_dataclass.class_schema(Picking)
AddressSchema = marshmallow_dataclass.class_schema(Address)
SaleLineSchema = marshmallow_dataclass.class_schema(Sale_Line)
SaleSchema = marshmallow_dataclass.class_schema(Sale)


class API:
    """This interacts with the external odoo 13 API with XMLrpc

       The API Class is intended to be used a black box implentation
       which is imported by another Class or Python Scipt
    """

    url = "http://localhost:8069"
    db = "anemoi_test"
    username = "test"  # FIXME Read Val from system env or file
    password = "1234"  # FIXME Read Val from system env or file
    common = xmlrpc.client.ServerProxy("{}/xmlrpc/2/common".format(url))
    models = xmlrpc.client.ServerProxy("{}/xmlrpc/2/object".format(url))
    uid = common.authenticate(db, username, password, {})

    def __init__(
        self, id, name, number, addres, description, menge, preis, sum_order, comment,
    ):
        """Reads the values from its parameters and uses them to build the API"""
        self.printed = True
        self.id = id
        self.name = name
        self.number = number
        self.addres = addres
        self.description = description
        self.menge = menge
        self.preis = preis
        self.sum_order = sum_order
        self.comment = comment

    def login():
        """Login into the Server return the reading rights of stock.picking"""
        # To verify if the connection information is correct before trying to authenticate,
        # the simplest call is to ask for the server’s version.
        print(API.common.version())

        # The second endpoint is xmlrpc/2/object, is used to call
        # methods of odoo models via the execute_kw RPC function.
        return API.models.execute_kw(
            API.db,
            API.uid,
            API.password,
            "stock.picking",
            "check_access_rights",
            ["read"],
            {"raise_exception": False},
        )

    def get_fields(field_name):
        """What Fields does the our Odoo Model have of of this data Field"""
        return API.models.execute_kw(
            API.db,
            API.uid,
            API.password,
            field_name,
            "fields_get",
            [],
            {"attributes": ["string", "help", "type"]},
        )

    def get_partner_address(ids):
        """Returns the adress and geolocation of all ids as a AdressSchema"""
        ps = API.models.execute_kw(
            API.db,
            API.uid,
            API.password,
            "res.partner",
            "read",
            [ids],
            {
                "fields": [
                    "name",
                    "street",
                    "street2",
                    "zip",
                    "phone",
                    "city",
                    "partner_latitude",
                    "partner_longitude",
                ]
            },
        )
        p_objects = [AddressSchema().load(p) for p in ps]
        return p_objects

    def get_not_printed_pickings():
        """Returns all Pickings which have not been not been printed or processed
           The xml Response of the Server is parsed to a python Data Picking Class """
        ps = API.models.execute_kw(
            API.db,
            API.uid,
            API.password,
            "stock.picking",
            "search_read",
            [
                ["&", ("printed", "!=", True), ("state", "!=", "cancel")]
            ],  # TODO add check if adress is null.
            {
                "fields": [
                    "name",
                    "partner_id",
                    "sale_id",
                    "state",
                    "create_date",
                    "printed",
                    "product_id",
                    "move_lines",
                    "location_id",
                    "location_dest_id",
                    "group_id",
                ],
                "limit": 50,
            },
        )
        pprint.pprint(ps)
        p_objects = [PickingSchema().load(p) for p in ps]
        return p_objects

    def __load_picking(p_picking):
        """Loads the Picking Schema from a String. Usefull for testing"""
        picking_schema = marshmallow_dataclass.class_schema(Picking)
        return picking_schema.load(p_picking)

    def get_product(ids):
        """Returns all orders Lines of a Product"""
        return API.models.execute_kw(
            API.db,
            API.uid,
            API.password,
            "product.product",
            "read",
            [ids],
            {"fields": ["order_line",]},
        )

    def post_printed_picking(printed: bool, picking_id: int):
        """Post the new value of printed for picking_id"""
        API.models.execute_kw(
            API.db,
            API.uid,
            API.password,
            "stock.picking",
            "write",
            [[picking_id], {"printed": printed}],
        )
